package edu.umb.cs.antmanager.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.UnknownHostException;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import edu.umb.cs.antmanager.common.AgentInterface;
import edu.umb.cs.antmanager.common.MachineStatus;
import edu.umb.cs.antmanager.common.ManagerInterface;
import edu.umb.cs.antmanager.common.RMIServerConnector;
import edu.umb.cs.antmanager.domain.TestType;
import static edu.umb.cs.antmanager.common.Constants.*;

public class RMIAgentServer extends UnicastRemoteObject implements
		AgentInterface {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private ReadWriteLock lock;
	private static RMIAgentServer server;
	private MachineStatus machineStatus;
	private String managerName;

	public static synchronized RMIAgentServer createRMIAgent()
			throws RemoteException {
		if (server == null)
			server = new RMIAgentServer();
		return server;
	}

	private RMIAgentServer() throws RemoteException {
		super();
		try {
			machineStatus = new MachineStatus(java.net.InetAddress
					.getLocalHost().getHostName());
		} catch (UnknownHostException e) {
			// TODO host name unavailable
			System.err.println("cannot get agent's host name");
		}
		machineStatus.setStatus(STATUS_IDLE);
		lock = new ReentrantReadWriteLock();
	}

	private static void init() {
		System.setProperty("java.security.policy", SECURITY_FILE);
		System.setProperty("java.rmi.server.codebase", CODE_BASE);
		if (System.getSecurityManager() == null) {
			System.setSecurityManager(new SecurityManager());
		}
		try {
			LocateRegistry.createRegistry(SERVER_PORT);
		} catch (RemoteException e) {
			// There is a running Registry on that port, don't need
			// to create another one.
		}
	}

	public static void startServer() {
		init();
		try {
			Registry registry = LocateRegistry.getRegistry(SERVER_PORT);
			RMIAgentServer agent = RMIAgentServer.createRMIAgent();
			registry.bind(AGENT_SERVER_NAME, agent);
		} catch (AccessException e) {
			// TODO the Registry is denied the agen't binding request
			System.err.println("Cannot bind the agent to the registry");
		} catch (RemoteException e) {
			// TODO the Registry is not acessible
			System.err.println("The agent's registry isnot avaialable");
		} catch (AlreadyBoundException e) {
			// TODO There is another agent running on this machine
			System.err.println("You cannot run more than one agents on the same machine!");
			System.err.println("Please shutdwon the running agent first!");
			System.err.println("This window will close automatically in 10 seconds.");
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			System.exit(0);
		}
	}

	public static void main(String[] args) {
		RMIAgentServer.startServer();
	}

	protected void freeServer() {
		lock.writeLock().lock();
		try {
			// send result back to the manager
			ManagerInterface manager = RMIServerConnector
					.createManagerConnector(managerName);
			File file = new File(ANT_MANAGER_HOME+"\\"+machineStatus.getRunningTest().getName()
					+ ".xml");
			FileInputStream is = new FileInputStream(file);
			byte[] result = new byte[(int) file.length()];
			is.read(result);
			manager.addTestLog(result, machineStatus);
			machineStatus.setStatus(STATUS_IDLE);
		} catch (IOException e) {
			//TODO result log missing
			System.err.println(machineStatus.getRunningTest().getName()+".xml was not found");
		} catch (NotBoundException e) {
			//TODO cannot connect the manager(manager dead?)
			System.err.println("Cannot connect to the manager");
		} finally {
			lock.writeLock().unlock();
		}
	}

	@Override
	public MachineStatus getStatus() throws RemoteException {
		lock.readLock().lock();
		try {
			return machineStatus;
		} finally {
			lock.readLock().unlock();
		}
	}

	@Override
	public boolean excuteTask(TestType test, String manager)
			throws RemoteException {
		lock.writeLock().lock();
		// TODO should the agent check the test's resource requirement?
		try {
			if (machineStatus.getStatus().equals(STATUS_IDLE)&&test!=null) {
				new Thread(new AntRunner(test, this)).start();
				machineStatus.setStatus(STATUS_BUSY);
				machineStatus.setRunningTest(test);
				this.managerName = manager;
				return true;
			}
			return false;
		} finally {
			lock.writeLock().unlock();
		}
	}

	@Override
	public void shutDown() throws RemoteException {
		try {
			Registry registry = LocateRegistry.getRegistry();
			registry.unbind(AGENT_SERVER_NAME);
			UnicastRemoteObject.unexportObject(server, true);
			server = null;
		} catch (NotBoundException e) {
			// Nothing to do, as the agent is already shutdown
		}

	}

	@Override
	public void restart() throws RemoteException {
		shutDown();
		RMIAgentServer.startServer();
	}
}
